'''
dispatcher.py

Copyright 2009 Xavier Mendez Navarro aka Javi

This file is part of pysqlin

pysqlin is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation version 2 of the License.

pysqlin is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with pysqlin; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
'''

from UserDict import UserDict
from collections import defaultdict
import logging
import re

class MyUserDict(UserDict):
    def __init__(self):
	self.__logger = logging.getLogger("ui.console.MyUserDict")

	#UserDict.__init__(self)

	self.data = defaultdict(list)

    def __setitem__(self, key, value):
	if value is not None:
	    self.data[key].append(value)
	else:
	    self.data[key] = []
    
    def __getitem__(self, asking_key):
	self.__logger.debug('__getitem__. START key=%s', asking_key)
	self.__logger.debug('__getitem__. all keys=%s"', self.data.keys())

	# try to answer normal keys first
	key_matches = [key_event for key_event in self.data.keys() if key_event.split()[1].find('*')==-1 and re.findall(key_event.split()[1]+"$", asking_key)]
	self.__logger.debug('__getitem__. normal key matches=%s"', key_matches)
	if key_matches:
	    key_matched = key_matches[0]
	    if self.data[key_matched]:
		self.__logger.debug('__getitem__. asking key=%s', key_matched) 
		return self.data[key_matched]
	    else:
		self.__logger.debug('__getitem__. normal asking key matched but event not defined => continue')

	dynamic_keys = [k for k in self.data.keys() if k.find('*') >= 0 ]

	self.__logger.debug('__getitem__. dynamic keys=%s"', dynamic_keys)

	organized_events = {
	    'COMMAND': [],
	    'PARAMETER': []
	}

	for i in dynamic_keys:
	    event_type, event = i.split()
	    regex_event = event.replace('.', '\.')
	    regex_event = regex_event.replace('*', '.*')
	    organized_events[event_type].append((event, regex_event))

	event_type, event = asking_key.split()

	self.__logger.debug('__getitem__. organized dictionary keys=%s"' % (organized_events))
	self.__logger.debug('__getitem__. asking for key: event_type=%s, event=%s"' % (event_type, event))

	dynamic_key_matches = [key_event for key_event, key_regex in organized_events[event_type] if re.findall(key_regex, asking_key)]
	#dynamic_key_matches = list(set(dynamic_key_matches)) # delete duplicates

	if not dynamic_key_matches:
	    return None
	else:
	    dynamic_key_matches.sort(lambda x, y: y.find('*')-x.find('*'))
	    self.__logger.debug('__getitem__. asking key: dynamic matches=%s"', dynamic_key_matches)
	    # if exists more than one key match. return the most concrete => * at the rigth.
	    self.__logger.debug('__getitem__. asking key=%s', event_type + " " + dynamic_key_matches[0]) 
	    return self.data[event_type + " " + dynamic_key_matches[0]]


class EventDispatcher:
    def __init__(self):
	self.__logger = logging.getLogger("framework.EventDispatcher")
	self.publisher = MyUserDict()

    def create_event(self, msg):
	self.publisher[msg] = None

    def subscribe(self, func, msg, dynamic = False):
	#if not self.publisher.has_key():
	#    self.publisher[msg] = []
	#self.publisher[msg].append(func)
	if not self.publisher.has_key(msg) and not dynamic:
	    self.__logger.error('subscribe. No such event: %s', msg)
	    raise KeyError, 'subscribe. No such event: %s' % (msg)
	else:
	    self.__logger.debug('subscribe. Subscribed to Event: %s', msg)
	    self.publisher[msg] = func

    def notify(self, msg, **event):
	self.__logger.debug('notify. START msg: %s', msg)

	if not self.publisher.has_key(msg):
	    self.__logger.error('notify. No such event: %s', msg)
	    raise KeyError, 'notify. Event not subscribed: %s' % (msg,)
	elif not self.publisher[msg]:
	    self.__logger.error('notify. Event not subscribed: %s', msg)
	else:
	    for functor in self.publisher[msg]:
		try:
		    msg =  functor(**event)
		    if msg: return msg
		except Exception, e:
		    self.__logger.error('notify. Unhandled exception executing command: %s' % e)
		    return 'Unhandled exception executing command: %s' % e
